package com.sojson.util.lock.impl;

import static com.sojson.util.lock.LockUtil.TIMEOUT_LONG;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

import com.sojson.config.exception.TransErrorCode;
import com.sojson.util.ExpUtil;
import com.sojson.util.StringUtil;
import com.sojson.util.lock.CustomLock;
import com.sojson.util.lock.DistributedLockUtilInterface;
import com.sojson.util.lock.LockUtil;

/**
 * ReentrantLock本地锁工具类实现类
 * 
 * @author liu
 * @date 2021-08-04
 */
@SuppressWarnings("unused")
public class ThisLockReentrantLockUtilImpl implements DistributedLockUtilInterface {

    private static ThisLockReentrantLockUtilImpl thi;
    /** 锁集合 */
    Map<String, ReentrantLock> locks = new HashMap<>();
    /** 非公平锁 */
    private ReentrantLock lock = new ReentrantLock();
    /** 公平锁 */
    // private ReentrantLock lock = new ReentrantLock(true);
    /** 锁对象 */
    private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    /** 读锁 */
    private ReadLock readLock = reentrantReadWriteLock.readLock();
    /** 写锁 */
    private WriteLock writeLock = reentrantReadWriteLock.writeLock();

    /**
     * ReentrantLock锁实现类
     * 
     * @author liu
     * @date 2020-11-15
     */
    class CustomLockReentrantLock implements CustomLock {

        /** 当前锁名称 */
        private String lockName;
        /** 非公平锁 */
        private ReentrantLock lock;

        public CustomLockReentrantLock(String lockName) {
            synchronized (DistributedLockUtilInterface.class) {
                this.lockName = getLockName(lockName);
                lock = locks.get(this.lockName);
                if (StringUtil.isBlankObject(lock)) {
                    lock = createLock();
                    locks.put(this.lockName, lock);
                }
            }
        }

        /**
         * 在给定的时间内获取锁,如果获取到返回true,超时或者没有获取到返回false
         * 
         * @param timeOutGet    获取锁的超时时间(单位: 秒,不设置为10秒)
         * @param timeOutUse    使用锁的超时时间(单位: 秒,不设置为30秒)
         * @param unit          锁的超时时间单位
         * @return
         * @throws Exception 
         */
        @Override
        public boolean lock(long timeOutGet, long timeOutUse, TimeUnit unit) throws Exception {
            boolean isGetLock = false;
            if (timeOutGet == TIMEOUT_LONG && timeOutUse == TIMEOUT_LONG) {
                lock.lock();
                isGetLock = true;
            } else if (timeOutGet != TIMEOUT_LONG && timeOutUse == TIMEOUT_LONG) {
                isGetLock = lock.tryLock(timeOutGet, unit);
            } else if (timeOutGet == TIMEOUT_LONG && timeOutUse != TIMEOUT_LONG) {
                lock.lock();
                isGetLock = true;
            } else {
                isGetLock = lock.tryLock(timeOutGet, unit);
            }

            // 如果获取到了锁就返回
            if (isGetLock) {
                return true;
            }
            // 如果没获取到就抛异常
            ExpUtil.throwEx(TransErrorCode.THREAD_BUSY);
            return false;
        }

        /**
         * 释放锁
         */
        @Override
        public void unlock() {
            boolean heldByCurrentThread = lock.isHeldByCurrentThread();
            if (heldByCurrentThread) {
                lock.unlock();
                return;
            }

            ExpUtil.throwEx(TransErrorCode.UNLOCK_FAIL);
        }

        /**
         * 创建锁
         * 
         * @return
         */
        private ReentrantLock createLock() {
            return new ReentrantLock();
        }

        public String getLockName(String lockName) {
            return LockUtil.CACHE_NAME_PREFIX_LOCK + lockName;
        }
    }

    @Override
    public CustomLock getLock(String lockName) {
        return new CustomLockReentrantLock(lockName);
    }

    /**
     * 判断一个对象是否是空(空对象)
     * 
     * @param obj
     * @return
     */
    private static boolean isBlankObject(Object obj) {
        return StringUtil.isBlankObject(obj);
    }

    public static ThisLockReentrantLockUtilImpl getInstances() {
        if (isBlankObject(thi)) {
            thi = new ThisLockReentrantLockUtilImpl();
        }
        return thi;
    }

}